import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:math' show Random;
import 'package:flutter_elf/utils/print_utils.dart';

import 'package:event_bus/event_bus.dart';
import 'package:flutter_elf/_base/build_config.dart';
import 'package:flutter_elf/localstorage/local_storage.dart';
import 'package:flutter_elf/model/user.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:rxdart/rxdart.dart';

import '../main.dart';
import 'api.dart';

class SocketCmd {
  SocketCmd._();

  static const CmdId_ConnBeatHeart_CS = 20001, //心跳
      CmdId_ConnBeatHeart_SC = 20002,
      CmdId_ConnLogin_CS = 20003, //登录
      CmdId_ConnLogin_SC = 20004,
      CmdId_ForcedExit_SC = 20501, //多手机登录，强制退出消息
      CmdId_OderStatus_SC = 27501, //订单状态
      CmdId_TransferMessage_CS = 20005,
      CmdId_TransferMessage_SC = 20006,
      CmdId_PushMessage_CS = 20007,
      CmdId_PushMessage_SC = 20008,
      CmdId_Publish_CS = 20009,
      CmdId_Publish_SC = 20010,
      CmdId_SendByUserId_CS = 20011,
      CmdId_SendByUserId_SC = 20012,
      CmdId_SendToAll_CS = 20013,
      CmdId_SendToAll_SC = 20014,
      // CmdId_GetAllPublish_CS           = 20015,
      // CmdId_GetAllPublish_SC           = 20016,
      CmdId_RegisterObserver_CS = 20017,
      CmdId_RegisterObserver_SC = 20018,
      // CmdId_GetMyPublish_CS            = 20019,
      // CmdId_GetMyPublish_SC            = 20020,
      CmdId_DelMyPublish_CS = 20021,
      CmdId_DelMyPublish_SC = 20022,
      CmdId_KickUser_CS = 20023,
      CmdId_CreateOrder = 25419,
      CmdId_CancelOrder = 25413,
      CmdId_FinishOrder = 25417,
      CmdId_KickUser_SC = 20024,
      CmdId_IM_SC = 35006; //IM推送

  static const TopicId_Trading = 10001, //实时交易
      TopicId_OrderBook = 10002, //推送盘口数据
      TopicId_Market = 10003, //k线行情
      TopicId_OneDayDeal = 10004, //24小时成交量
      TopicId_Changes = 10006; //涨幅榜

}

enum _State { connecting, open, reconnect, closed }

class SocketIO {
  final EventBus _eventBus = EventBus();
  static final SocketIO instance = SocketIO._internal();

  factory SocketIO() => instance;

  SocketIO._internal();

  final String wsUrl = Api.wsUrl;
  bool _isSendHeart = false;
  _State _readyState = _State.reconnect;
  WebSocket _webSocket;
  Timer _timer;

  void connect() {
    if (_readyState != _State.reconnect) return;
    Observable.fromFuture(WebSocket.connect(wsUrl))
        .doOnListen(() {
          _log("connect", "连接...");
          _readyState = _State.connecting;
        })
        .timeout(Duration(seconds: 20), onTimeout: (_) {
          _log("onTimeout", "连接超时");
          _?.close();
          _reconnect();
        })
        .doOnData((socket) {
          _log("connect", "连接成功");
          _readyState = _State.open;
          _webSocket = socket;
          login();
          _resub();
          _initTimer();
        })
        .flatMap((socket) => Observable(socket))
        .listen(_onMessage, onError: (e, StackTrace stackTrace) {
          _log("onError", "异常：$e   =====stackTrace:$stackTrace");
          _reconnect();
        }, onDone: () {
          _log("onDone", "断开了");
          _reconnect();
        });
  }

  ///关闭  isReconn：关闭后是否重连
  void close() async {
    _log("close", "关闭");
    _readyState = _State.closed;
    _timer?.cancel();
    await _webSocket?.close();
  }

//  {"iCmdId":20002,"iCmdSeq":0,"iRet":0,"sData":{"iRet":0}}
  _onMessage(msg) {
//    _log("_onMessage", msg?.toString()?.replaceAll(" ", "")?.replaceAll("\n", ""));
    try {
      Map msgJson = (msg is Map) ? msg : msg != null ? json.decode(msg) : new Map();

//      MessageInfoResponse msgRes = MessageInfoResponse.fromJson(msgJson);
//      if (msgRes?.sData == null) return;
//      var dataJson = msgRes.sData;
//      switch (msgRes.iCmdId) {
//        case SocketCmd.CmdId_ConnBeatHeart_SC: //心跳
//          _isSendHeart = false;
//          break;
//        case SocketCmd.CmdId_ConnLogin_SC:
//          break;
//        case SocketCmd.CmdId_TransferMessage_SC: //消息成功
//          _log("socket:", '发送消息成功');
//          break;
//        case SocketCmd.CmdId_PushMessage_SC: //收到消息
//          _log('socket:收到消息');
////        MsgResponse decodeMsg = MsgResponse.fromJson(dataJson);
////        if (currentConversationUser != decodeMsg.stMessageInfo.iFromId) {
////          _pushLocalNotification('牡丹服务员', decodeMsg.stMessageInfo.sContent, message.toString());
////        }
//          break;
//        case SocketCmd.CmdId_SendToAll_CS:
//          if (dataJson != null) _handleTopicMsg(dataJson);
//          break;
//        case SocketCmd.CmdId_CreateOrder:
//          _dispatch(CreateOrder.fromJson(msgJson));
//          break;
//        case SocketCmd.CmdId_CancelOrder:
//          _dispatch(CancelOrder.fromJson(msgJson));
//          break;
//        case SocketCmd.CmdId_FinishOrder:
//          _dispatch(FinishOrder.fromJson(msgJson));
//          break;
//        case SocketCmd.CmdId_IM_SC: //聊天
//          _dispatch(VMessageInfo.fromJson(dataJson));
//          break;
//        case SocketCmd.CmdId_ForcedExit_SC:
//          var forcedExit = ForcedExit.fromJson(dataJson);
//          if (forcedExit.sPlatform == 'app') _dispatch(forcedExit);
//          break;
//        case SocketCmd.CmdId_OderStatus_SC:
//          _dispatch(OrderState.fromJson(dataJson));
//          break;
//      }
    } on FormatException catch (_) {}
  }

  //处理订阅消息
  void _handleTopicMsg(dataJson) {
    var iTopicId = dataJson['iTopicId'];
//    switch (iTopicId) {
//      case SocketCmd.TopicId_Trading: //实时成交
//        _dispatch(TopicTrading.fromJson(dataJson));
//        break;
//      case SocketCmd.TopicId_OrderBook: //推送盘口数据
//        _dispatch(TopicOrderBook.fromJson(dataJson));
//        break;
//      case SocketCmd.TopicId_Market: //k线行情
//        _dispatch(TopicMarket.fromJson(dataJson));
//        break;
//      case SocketCmd.TopicId_OneDayDeal: //24小时成交量(单个或多个)
//        _dispatch(TopicOneDayDeal.fromJson(dataJson));
//        break;
//      case SocketCmd.TopicId_Changes:
//        _dispatch(TopicChanges.fromJson(dataJson));
//        break;
//    }
  }

  //异常重连
  _reconnect() {
    if (_readyState == _State.reconnect || _readyState == _State.closed) return;
    _readyState = _State.reconnect;
    _timer?.cancel();
    _webSocket?.close();
    Timer(Duration(seconds: 5), () {
      _log("_reconnect", "开始重连");
      connect();
    });
  }

  //心跳
  _initTimer() {
    _timer?.cancel();
    _timer = Timer.periodic(Duration(seconds: 30), (_) {
      bool isSuccess = send(SocketCmd.CmdId_ConnBeatHeart_CS, {"iUserId": 110});
      if (!isSuccess || _isSendHeart) {
        // 如果没有收到下次心跳，重新连接
        _isSendHeart = false;
        _reconnect();
      } else {
        _isSendHeart = true;
      }
    });
  }

  void _dispatch(data) => _eventBus.fire(data);

  ///监听
  StreamSubscription<T> listen<T>(void onData(T event), {Function onError, void onDone(), bool cancelOnError}) {
    return _eventBus.on<T>().listen(onData, onError: onError, onDone: onDone, cancelOnError: cancelOnError);
  }

  login() {
    User _user = LocalStorage.getUser();
    if (_user != null && _user.iUserId != null) {
      Map map = {"iUserId": _user.iUserId, "sSessionKey": _user.sSessionKey, "sPlatform": "app"};
      send(SocketCmd.CmdId_ConnLogin_CS, map);
    }
  }

  //退出登录是调用
  logOut() {
    _subQueue.clear();
    _readyState = _State.reconnect;
    _webSocket?.close();
  }

  //发送消息
  bool send(int cmd, params) {
    if (_readyState != _State.open) {
//      _eventBus?.streamController?.addError("发送失败");
      _log("send", "发送失败:${params}");

      return false;
    } else {
      Map data = {"iCmdId": cmd, "iCmdSeq": 0};
      data["sData"] = json.encode(params ?? {});
      _log("send", json.encode(data));
      _webSocket?.add(json.encode(data));
      return true;
    }
  }

  //订阅队列，处理多个页面订阅同一个指令
  List<String> _subQueue = [];

  ///socket订阅消息
  ///subs：多个订阅
  ///sub：单个订阅
  bool sub({List<String> subs, String sub}) {
    if (subs == null && sub == null) throw "SocketIO.sub 参数不能全为null";
    List<String> list;
    if (subs != null && sub != null)
      list = subs..add(sub);
    else if (subs != null)
      list = subs;
    else if (sub != null) list = [sub];
    //去除已经订阅的
    var newList = list.where((s) => !_subQueue.contains(s)).toList();
    //添加到订阅队列
    _subQueue.addAll(list);
    //没有空，说明之前都已经订阅
//    _log("_subQueue_sub:",_subQueue);
    if (newList.isEmpty) return true;
    return send(SocketCmd.CmdId_RegisterObserver_CS, {"vSubject": newList});
  }

  ///socket取消订阅
  ///subs：多个取消
  ///sub：单个取消
  bool unsub({List<String> subs, String sub}) {
    if (subs == null && sub == null) throw "SocketIO.unsub 参数不能全为null";
    List<String> list;
    if (subs != null && sub != null)
      list = subs..add(sub);
    else if (subs != null)
      list = subs;
    else if (sub != null) list = [sub];
    //每一次取消订阅从订阅队列去除一个
    list.forEach((s) => _subQueue.remove(s));
    //当订阅队列没有这个订阅了，才真正通知服务器取消订阅
    list = list.where((s) => !_subQueue.contains(s)).toList();
//    _log("_subQueue_unsub:",_subQueue);
    if (list.isEmpty) return true;
    return send(SocketCmd.CmdId_DelMyPublish_CS, {"vSubject": list});
  }

  ///重连后去重 重新订阅
  void _resub() {
    if (_subQueue.isEmpty) return;
    List list = _subQueue.toSet().toList();
    send(SocketCmd.CmdId_RegisterObserver_CS, {"vSubject": list});
  }

  Future<void> _pushLocalNotification(String title, String content, String payload, [int id = 0]) async {
    var androidPlatformChannelSpecifics = AndroidNotificationDetails(
        'your channel id', 'your channel name', 'your channel description',
        importance: Importance.Max, priority: Priority.High, ticker: 'ticker');
    var iOSPlatformChannelSpecifics = IOSNotificationDetails();
    var platformChannelSpecifics = NotificationDetails(androidPlatformChannelSpecifics, iOSPlatformChannelSpecifics);
    await flutterLocalNotificationsPlugin
        .show(id == null ? Random().nextInt(6) : id, title, content, platformChannelSpecifics, payload: payload);
  }

  _log(String tag, [msg]) {
    if (BuildConfig.isDebug) globalPrint("SocketIO====>$tag：$msg");
  }
}
